/*
 * @(#)AntWorld.java			0.1 24/11/2010
 * Copyright (C) 2010 Grupo de Alunos abaixo
 * 
 * Universidade Federal do ABC
 * Vida Artificial - 3o quadrimestre de 2010
 * Aluno: Abner
 * Aluno: Cláudio Antônio Peanho
 * Aluno: Fabrizio Borelli
 * Aluna: Maitê Balhester
 * Aluno: Rafael Munhoz
 * Professora Dr. Karla Vittori
 * Projeto: Simulação de Colônia de Formigas (antcolonyswarm)
 * 
 * Código baseado em jheatbugs-3.0 de Timothy Howe
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package br.edu.ufabc.va.acs.model;

import java.awt.Point;
import java.util.Properties;

import br.edu.ufabc.va.acs.model.agent.Ant;
import br.edu.ufabc.va.acs.model.agent.Food;

import swarm.defobj.Zone;

import swarm.space.Diffuse2d;
import swarm.space.Diffuse2dImpl;
import swarm.space.Discrete2d;
import swarm.space.Discrete2dImpl;

/**
 * @author Peanho
 * 
 */
public class SingleGridWorld implements AntWorld {

	/** largura da grade bidimensional */
	private final int			width;

	/** altura da grade bidimensional */
	private final int			height;

	/** grade bidimensional que armazena a posição das formigas */
	private final Discrete2d	antGrid;

	/** grade bidimensional que armazena a comida */
	private final Discrete2d	foodGrid;

	/** armazena intensidade de feromônios (inteiro) em uma posição da grade */
	private final Diffuse2d		pheromoneSpace;

	/**
	 * Constrói e inicializa uma instância bidimensional do mundo com largura
	 * {@code width}, altura {@code height}, constante de difusão do feromônio
	 * depositado pelas formigas {@code diffusionConstant} e taxa de evaporação
	 * de feromônio no ambiente {@code evaporationRate}.
	 * 
	 * @param zone
	 *            refência a área de memória em que este objeto irá existir.
	 * @param width
	 *            largura da grade que representa o mundo
	 * @param height
	 *            altura da grade que representa o mundo
	 * @param diffusionConstant
	 *            constante de difusão aplicada ao feromônio depositado na grade
	 * @param evaporationRate
	 *            taxa de evaporação do feromônio depositado na grade
	 * 
	 */
	public SingleGridWorld(Zone zone, int width, int height,
			int diffusionConstant, int evaporationRate) {
		this.width = width;
		this.height = height;
		this.antGrid = new Discrete2dImpl(zone, width, height);
		this.pheromoneSpace = new Diffuse2dImpl(zone, width, height,
				diffusionConstant, evaporationRate);
		this.foodGrid = new Discrete2dImpl(zone, width, height);
	}

	/**
	 * Constroi e configura uma nova instância da classe com os parâmetros em
	 * {@code configuration}.
	 * 
	 * @param zone
	 *            area de memória em que este objeto irá residir
	 * @param configuration
	 *            mapa de valores de configuração
	 */
	public SingleGridWorld(Zone zone, Properties configuration) {
		this.width = Integer.parseInt(configuration.getProperty("world.width"));
		this.height = Integer.parseInt(configuration
				.getProperty("world.height"));
		this.antGrid = new Discrete2dImpl(zone, width, height);
		this.foodGrid = new Discrete2dImpl(zone, width, height);
		double diffusionConstant = Double.parseDouble(configuration
				.getProperty("world.diffusionConstant"));
		double evaporationRate = Double.parseDouble(configuration
				.getProperty("world.evaporationRate"));
		this.pheromoneSpace = new Diffuse2dImpl(zone, width, height,
				diffusionConstant, evaporationRate);
		pheromoneSpace.updateLattice();

		/* carrega o mapa do ambiente */

		/* carrega o mapa com a posição da comida */

		boolean loadPheromoneSpace = Boolean.parseBoolean(configuration
				.getProperty("world.load-pheromone-space"));
		if (loadPheromoneSpace)
			pheromoneSpace.setDiscrete2d$toFile(pheromoneSpace,
					configuration.getProperty("world.pheromone-space-file"));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#isEmptyPosition(int, int)
	 */
	@Override
	public boolean isPositionEmpty(int x, int y) {
		if (x > 0 && x < width && y > 0 && y < height)
			return antGrid.getObjectAtX$Y(x, y) == null;
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#isPositionEmpty(java.awt.Point)
	 */
	@Override
	public boolean isPositionEmpty(Point pos) {
		if (pos.x > 0 && pos.x < width && pos.y > 0 && pos.y < height)
			return antGrid.getObjectAtX$Y(pos.x, pos.y) == null;
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#move(java.awt.Point,
	 * java.awt.Point)
	 */
	@Override
	public synchronized void move(Ant ant, Point from, Point to) {
		antGrid.putObject$atX$Y(null, from.x, from.y);
		antGrid.putObject$atX$Y(ant, to.x, to.y);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#depositPheromone(java.awt.Point)
	 */
	@Override
	public synchronized void depositOnePheromoneUnit(Point pos) {
		int amount = pheromoneSpace.getValueAtX$Y(pos.x, pos.y);
		amount = amount > 29767 ? 32767 : amount + 20000;
		pheromoneSpace.putValue$atX$Y(amount, pos.x, pos.y);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#depositPheromone(java.awt.Point,
	 * int)
	 */
	@Override
	public void depositPheromone(Point pos, int amount) {
		int value = pheromoneSpace.getValueAtX$Y(pos.x, pos.y);
		value += amount;
		pheromoneSpace.putValue$atX$Y(value, pos.x, pos.y);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#modelStep()
	 */
	@Override
	public Object modelStep() {
		pheromoneSpace.stepRule();
		return this;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#getWidth()
	 */
	@Override
	public int getWidth() {
		return width;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#getHeight()
	 */
	@Override
	public int getHeight() {
		return height;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#getPheromoneSpace()
	 */
	@Override
	public Diffuse2d getPheromoneSpace() {
		return pheromoneSpace;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#getWorld()
	 */
	@Override
	public Discrete2d getWorld() {
		return antGrid;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * br.edu.ufabc.va.acs.model.AntWorld#getPheromoneValueAt(java.awt.Point)
	 */
	@Override
	public int getPheromoneValueAt(Point pos) {
		if (pos.x > 0 && pos.x < width && pos.y > 0 && pos.y < height)
			return pheromoneSpace.getValueAtX$Y(pos.x, pos.y);
		else
			return -1;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#getPheromoneValueAt(int, int)
	 */
	@Override
	public int getPheromoneValueAt(int x, int y) {
		if (x > 0 && x < width && y > 0 && y < height)
			return pheromoneSpace.getValueAtX$Y(x, y);
		else return -1;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#isFood(java.awt.Point)
	 */
	@Override
	public boolean isFood(Point pos) {
//		Object object = antGrid.getObjectAtX$Y(pos.x, pos.y);
//		return object != null && object instanceof Food;
		return foodGrid.getObjectAtX$Y(pos.x, pos.y) != null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#isFood(int, int)
	 */
	@Override
	public boolean isFood(int x, int y) {
		return foodGrid.getObjectAtX$Y(x, y) != null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#collectFood(int, int)
	 */
	@Override
	public void collectFood(int x, int y) {
		Food food = (Food) foodGrid.getObjectAtX$Y(x, y);
		food.decreaseOneUnit();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#getScenario()
	 */
	@Override
	public Discrete2d getScenario() {
		return antGrid;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.edu.ufabc.va.acs.model.AntWorld#updateLattice()
	 */
	@Override
	public Object updateLattice() {
		pheromoneSpace.updateLattice();
		return this;
	}

	/* (non-Javadoc)
	 * @see br.edu.ufabc.va.acs.model.AntWorld#addFoodSourceAt(br.edu.ufabc.va.acs.model.agent.Food, int, int)
	 */
	@Override
	public void addFoodSourceAt(Food food, int i, int j) {
		foodGrid.putObject$atX$Y(food, i, j);
		
	}

	/* (non-Javadoc)
	 * @see br.edu.ufabc.va.acs.model.AntWorld#isValidPos(java.awt.Point)
	 */
	@Override
	public boolean isValidPos(Point pos) {
		return (pos.x > 0 && pos.x < width && pos.y > 0 && pos.y < height);
	}

}
